package org.dreamwork.persistence.jdo.dao.impl;

import org.dreamwork.persistence.IGenericDao;
import org.dreamwork.persistence.Parameter;
import org.dreamwork.persistence.Sort;
import org.dreamwork.util.IDataCollection;
import org.dreamwork.util.ListDataCollection;
import org.springframework.orm.jdo.support.JdoDaoSupport;

import javax.jdo.Query;
import java.io.Serializable;
import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: seth.yang
 * Date: 2010-8-6
 * Time: 16:24:59
 */
public class GenericJDODaoImpl<T, PK extends Serializable> extends JdoDaoSupport implements IGenericDao<T, PK> {
    protected Class<T> type;
    
    protected GenericJDODaoImpl (Class<T> type) {
        this.type = type;
    }

    public PK save (T o) {
        this.getJdoTemplate ().makePersistent (o);
        return null;
    }

    public void save (Collection<T> c) {
        this.getJdoTemplate ().makePersistentAll (c);
    }

    public void save (T... a) {
        this.getJdoTemplate ().makePersistentAll (Arrays.asList (a));
    }

    public void update (T o) {
        this.getJdoTemplate ().makePersistent (o);
    }

    public void update (Collection<T> c) {
        this.getJdoTemplate ().makePersistentAll (c);
    }

    public void update (T... a) {
        this.getJdoTemplate ().makePersistentAll (Arrays.asList (a));
    }

    public void delete (T o) {
        this.getJdoTemplate ().deletePersistent (o);
    }

    public void delete (Collection<T> c) {
        this.getJdoTemplate ().deletePersistentAll (c);
    }

    public void delete (T... a) {
        this.getJdoTemplate ().deletePersistentAll (Arrays.asList (a));
    }

    @SuppressWarnings ("unchecked")
    public T get (PK key) {
        return (T) this.getJdoTemplate ().getObjectById (type, key);
    }

    @SuppressWarnings ("unchecked")
    public T get (Collection<Parameter> p) {
/*
        String ql = "select from " + type.getName ();
        StringBuilder builder = new StringBuilder ();
        Map<String, Object> values = new HashMap<String, Object> ();
        for (Parameter a : p) {
            if (builder.length () == 0) builder.append (" where ");
            else builder.append (" && ");
            builder.append (toString(a));
            values.put (a.name, a.value);
        }
        ql += builder;
        Collection c = this.getJdoTemplate ().find (ql, values);
        Iterator iter = c.iterator ();
        if (iter.hasNext ()) return (T) iter.next ();
        return null;
*/
        List<T> list = find (p);
        if (list != null && list.size () > 0) return list.get (0);
        return null;
    }

    public T get (Sort sort, Parameter... parameters) {
        return get (sort, Arrays.asList (parameters));
    }

    public T get (Sort sort, Collection<Parameter> parameters) {
        List<T> list = find (Arrays.asList (sort), parameters);
        return list == null || list.size () == 0 ? null : list.get (0);
    }

    public boolean exists (PK id) {
        return get (id) != null;
    }

    public boolean exists (Parameter... parameters) {
        return get (parameters) != null;
    }

    public boolean exists (Collection<Parameter> parameters) {
        return get (parameters) != null;
    }

    public T get (Parameter... p) {
        return get (Arrays.asList (p));
    }

    public IDataCollection<T> find (int pageNo, int pageSize, Sort sort, Parameter... parameters) {
        return find (pageNo, pageSize, sort, Arrays.asList (parameters));
    }

    public IDataCollection<T> find (int pageNo, int pageSize, Sort sort, Collection<Parameter> parameters) {
        return find (pageNo, pageSize, Arrays.asList (sort), parameters);
    }

    public int executeUpdate (String hql, Parameter... parameters) {
        return executeUpdate (hql, Arrays.asList (parameters));
    }

    public int executeUpdate (String hql, Collection<Parameter> parameters) {
        Query query = this.getPersistenceManager ().newQuery (hql);
        Object o;
        if (parameters.size () == 0) {
            o = query.execute ();
        } else {
            boolean useMap = hql.toLowerCase ().indexOf (" parameters ") > 0;
            if (useMap) {
                o = getPersistenceManager ().newQuery (hql).executeWithMap (asMap (parameters));
            } else {
                o = getPersistenceManager ().newQuery (hql).executeWithArray (asArray (parameters));
            }
        }

        if (o instanceof Number) return ((Number) o).intValue ();
        return 0;
    }

    public List<T> find (Parameter... p) {
        return find (Arrays.asList (p));
    }

    @SuppressWarnings ("unchecked")
    public List<T> find (Collection<Parameter> p) {
/*
        String ql = "select from " + type.getName ();
        StringBuilder builder = new StringBuilder ();
        Map<String, Object> values = new HashMap<String, Object> ();
        for (Parameter a : p) {
            if (builder.length () == 0) builder.append (" where ");
            else builder.append (" && ");
            builder.append (toString(a));
            values.put (a.name, a.value);
        }
        ql += builder;
        Collection c = this.getJdoTemplate ().find (ql, values);
        List<T> list = new ArrayList<T> (c.size ());
        list.addAll (c);
        return list;
*/
        return find (null, p);
    }

    @SuppressWarnings ("unchecked")
    public List<T> find (Collection<Sort> s, Collection<Parameter> p) {
        String ql = "select from " + type.getName ();
        StringBuilder builder = new StringBuilder ();
        Map<String, Object> values = new HashMap<String, Object> ();
        for (Parameter a : p) {
            if (builder.length () == 0) builder.append (" where ");
            else builder.append (" && ");
            builder.append (toString(a));
            values.put (a.name, a.value);
        }

        if (s != null && s.size () > 0) {
            StringBuilder orders = new StringBuilder ();
            for (Sort t : s) {
                if (orders.length () == 0) orders.append (" order by ");
                else orders.append (", ");
                orders.append (t.fieldName).append (' ').append (t.direction);
            }
            ql += builder.toString () + orders;
        }
        return (List<T>) this.getJdoTemplate ().find (ql, values);
    }

    public List<T> find (Collection<Sort> s, Parameter... p) {
        return find (s, Arrays.asList (p));
    }

    public IDataCollection<T> find (int pageNo, int pageSize, Collection<Parameter> p) {
        return find (pageNo, pageSize, (Collection<Sort>)null, p);
    }

    public IDataCollection<T> find (int pageNo, int pageSize, Parameter... p) {
        return find (pageNo, pageSize, (Collection<Sort>)null, p);
    }

    @SuppressWarnings ("unchecked")
    public IDataCollection<T> find (int pageNo, int pageSize, Collection<Sort> s, Collection<Parameter> p) {
        String ql = "select from " + type.getName ();
        StringBuilder builder = new StringBuilder ();
        Set<Parameter> values = new HashSet<Parameter> ();
        for (Parameter a : p) {
            if (builder.length () == 0) builder.append (" where ");
            else builder.append (" && ");
            builder.append (toString (a));
            values.add (a);
        }
        StringBuilder orders = new StringBuilder ();
        if (s != null && s.size () > 0) for (Sort t : s) {
            if (orders.length () == 0) orders.append (" order by ");
            else orders.append (", ");
            orders.append (t.fieldName).append (' ').append (t.direction);
        }
        ql += builder.toString () + orders;
        Map<String, Object> xx = new HashMap<String, Object> ();
        if (values.size () > 0) {
            ql += "parameters ";
            int index = 0;
            for (Parameter a : values) {
                if (index != 0) ql += ", ";
                ql += a.name;
                xx.put (a.name, a.value);
                index ++;
            }
        }
        Query query = this.getPersistenceManager ().newQuery (ql);
//        query.declareParameters ();


        int start = ((pageNo - 1) * pageSize);
        if (start < 0) start = 0;
        int end = pageNo * pageSize - 1;
        if (end < pageSize) end = pageSize;
        query.setRange (start, end);
        List<T> list = (List<T>) query.executeWithMap (xx);
        ListDataCollection<T> dc = new ListDataCollection<T> ();
        dc.setData (list);
        dc.setPageNo (pageNo);
        dc.setPageSize (pageSize);
        return dc;
    }

    public IDataCollection<T> find (int pageNo, int pageSize, Collection<Sort> s, Parameter... p) {
        return find (pageNo, pageSize, s, Arrays.asList (p));
    }

    @SuppressWarnings ("unchecked")
    public List<T> executeQuery (String jdoQL, Collection<Parameter> p) {
        if (p.size () == 0) {
            return (List<T>) this.getPersistenceManager ().newQuery (jdoQL).execute ();
        }
        boolean useMap = jdoQL.indexOf (" parameters ") > 0;
        if (useMap) {
            Map<String, Object> map = new HashMap<String, Object> (p.size ());
            for (Parameter a : p) map.put (a.name, a.value);
            return (List<T>) this.getPersistenceManager ().newQuery (jdoQL).executeWithMap (map);
        } else {
            Object[] values = new Object[p.size ()];
            int index = 0;
            for (Parameter a : p) values [index ++] = a.value;
            return (List<T>) this.getPersistenceManager ().newQuery (jdoQL).executeWithArray (values);
        }
    }

    public IDataCollection executeQuery (int pageNo, int pageSize, String hql, Parameter... parameters) {
        return executeQuery (pageNo, pageSize, hql, Arrays.asList (parameters));
    }

    @SuppressWarnings ("unchecked")
    public IDataCollection executeQuery (int pageNo, int pageSize, String jdoQL, Collection<Parameter> parameters) {
        Query q = this.getPersistenceManager ().newQuery (jdoQL);
        boolean useMap = jdoQL.indexOf (" parameters ") > 0;
        int start = ((pageNo - 1) * pageSize);
        if (start < 0) start = 0;
        int end = pageNo * pageSize - 1;
        if (end < pageSize) end = pageSize;
        q.setRange (start, end);

        List<T> data;
        if (useMap) {
            data = (List<T>) getPersistenceManager ().newQuery (jdoQL).executeWithMap (asMap (parameters));
        } else {
            data = (List<T>) this.getPersistenceManager ().newQuery (jdoQL).executeWithArray (asArray (parameters));
        }
        ListDataCollection ldc = new ListDataCollection ();
        ldc.setData (data);
        ldc.setPageSize (pageSize);
        ldc.setPageNo (pageNo);
        return ldc;
    }

    @Override
    public IDataCollection executeQuery (boolean flag, int pageNo, int pageSize, String hql, Parameter... parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public int executeNativeUpdate (String sql, Parameter... parameters) {
//        Query query = getPersistenceManager ().
        return 0;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public int executeNativeUpdate (String sql, Collection<Parameter> parameters) {
        return 0;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public List<Map<String, Object>> executeNativeQuery (String sql, Parameter... parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public List<Map<String, Object>> executeNativeQuery (String sql, Collection<Parameter> parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public IDataCollection<Map<String, Object>> executeNativeQuery (int pageNo, int pageSize, String sql, Parameter... parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public IDataCollection<Map<String, Object>> executeNativeQuery (int pageNo, int pageSize, String sql, Collection<Parameter> parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public <C> C get (Class<C> type, Serializable pk) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public <C> void save (Class<C> type, C... instances) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public <C> void save (Class<C> type, Collection<C> instances) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public <C> void update (Class<C> type, C... instances) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public <C> void update (Class<C> type, Collection<C> instances) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    public Number executeScale (String hql, Collection<Parameter> parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public Number executeScale (String hql, Parameter... parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public Number executeNativeScale (String sql, Collection<Parameter> parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public Number executeNativeScale (String sql, Parameter... parameters) {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public List<T> executeQuery (String jdoQL, Parameter... p) {
        return executeQuery (jdoQL, Arrays.asList (p));
    }

    protected String toString (Parameter p) {
        switch (p.operator) {
            case BETWEEN :
                return p.fieldName + " between " + p.name + "1 AND " + p.name + "2";
            case EQ:
                return p.fieldName + " == " + p.name;
            case NE:
                return p.fieldName + " <> " + p.name;
            case LT:
                return p.fieldName + " < " + p.name;
            case LE :
                return p.fieldName + " <= " + p.name;
            case GT :
                return p.fieldName + " > " + p.name;
            case GE :
                return p.fieldName + " >= " + p.name;
            case IN :
                return p.fieldName + " in (" + p.name + ")";
            case NOT_IN :
                return p.fieldName + " not in (" + p.name + ")";
            case IS_NULL :
                return p.fieldName + " is null";
            case IS_NOT_NULL :
                return p.fieldName + " is not null";
            case LIKE :
                return p.fieldName + " like " + p.name;
            default :
                return "";
        }
    }

    private Map<String, Object> asMap (Collection<Parameter> parameters) {
        Map<String, Object> map = new HashMap<String, Object> (parameters.size ());
        for (Parameter p : parameters) map.put (p.name, p.value);
        return map;
    }

    private Object[] asArray (Collection<Parameter> parameters) {
        Object[] a = new Object[parameters.size ()];
        int index = 0;
        for (Parameter p : parameters) a[index ++] = p.value;
        return a;
    }
}